--------------------------------------------------------------------------------------------------------------------------------------------
-- SwitchF2 gives a way to bind hot keys to forms/stances
--------------------------------------------------------------------------------------------------------------------------------------------
SWITCHF2_SavedVars = {};

local DEFAULT_KEYS = { [0] = "ALT-Q",
					   [1] = "ALT-R",
					   [2] = "ALT-Z",
					   [3] = "ALT-T",
					   [4] = "ALT-V",
					   [5] = "CTRL-F5",
					   [6] = "CTRL-F6",
					   [7] = "CTRL-F7",
					   [8] = "CTRL-F8",
					   [9] = "CTRL-F9",
					   [10] = "CTRL-F10" };
					   
local PRE_DBG = "";

-- ###################################################### Forward declares
local SWF2_Lock;
local SWF2_UnLock;

local SWF2_Toggle_Configure;


-- ###################################################### Utility
local function debug(msg)
	if (DEFAULT_CHAT_FRAME ~= nil) then
		if (PRE_DBG ~= "") then
			DEFAULT_CHAT_FRAME:AddMessage(PRE_DBG);
		end
		DEFAULT_CHAT_FRAME:AddMessage(msg);
	else
		PRE_DBG = PRE_DBG .. msg .. "\n";
	end
end


local function GetSavedVar(name,def)
	local key = GetRealmName() .. "_" .. UnitName("player") .. "_" .. name;
	
	if (SWITCHF2_SavedVars[key] ~= nil) then
		return SWITCHF2_SavedVars[key];
	end
	return def;
end

local function SetSavedVar(name,val)
	local key = GetRealmName() .. "_" .. UnitName("player") .. "_" .. name;
	SWITCHF2_SavedVars[key] = val;
end

-- ###################################################### Main Addon
SWF2_Header_Frame = nil

local Loaded = false

local function CheckAura()
	for i = 1, GetNumShapeshiftForms() do
		local icon,name,active = GetShapeshiftFormInfo(i)
		if active then
			SWF2_Header_Frame:UpdateButtons(i)
		end
	end
end

local function CreateHeaderFrame()
	SWF2_Header_Frame = CreateFrame("CheckButton","SwitchFHeader",UIParent,"SecureHandlerStateTemplate,SecureActionButtonTemplate")
	SWF2_Header_Frame:SetMovable(1)
	SWF2_Header_Frame:EnableMouse(1)
	SWF2_Header_Frame:SetToplevel(1)
	SWF2_Header_Frame:ClearAllPoints()
	SWF2_Header_Frame:SetWidth(32)
	SWF2_Header_Frame:SetHeight(32)
	SWF2_Header_Frame:SetPoint("CENTER",UIParent,"CENTER",0,0)
	SWF2_Header_Frame:SetFrameStrata("LOW")
	SWF2_Header_Frame:RegisterForDrag("LeftButton")
	SWF2_Header_Frame:SetBackdropColor(1,1,1,1)
	SWF2_Header_Frame:Hide()
	local t = SWF2_Header_Frame:CreateTexture(nil,"BACKGROUND")
	t:SetTexture(.5,.5,.5,.4)
	t:SetAllPoints(SWF2_Header_Frame)
	SWF2_Header_Frame.icon = t
	
	SWF2_Header_Frame:SetScript("OnDragStart", function(this,btn)
										this:StartMoving()
									end)

	SWF2_Header_Frame:SetScript("OnDragStop", function(this)
										this:StopMovingOrSizing()
									end)
									
	SWF2_Header_Frame.StanceButtons    = {}
	SWF2_Header_Frame.UpdateTime       = 0
	SWF2_Header_Frame.LastActiveStance = 0
	SWF2_Header_Frame.SetIcon          = false
	
	SWF2_Header_Frame:SetScript("OnUpdate", function(self, elapsed)
												SWF2_Header_Frame.UpdateTime = SWF2_Header_Frame.UpdateTime + elapsed
												if SWF2_Header_Frame.UpdateTime < 20 then
													SetPortraitTexture(SWF2_Header_Frame.icon,"player")
													CheckAura()
												end
											end)
											
	SWF2_Header_Frame.UpdateButtons = function (self,new_active)
										-- print("In updatebuttons new active is: " .. new_active)
										local changed = 1
										if new_active == nil then
											new_active = 0
										end
										if self.LastActiveStance == nil then
											self.LastActiveStance = 1
										elseif self.LastActiveStance == new_active then
											changed = 0
										end
										if self.LastActiveStance > 0 and changed == 1 then
											local icon,name,active = GetShapeshiftFormInfo(self.LastActiveStance)
											self.StanceButtons[self.LastActiveStance].icon:SetTexture(icon)
											if self.StanceButtons[self.LastActiveStance].grid:IsShown() then
												self.StanceButtons[self.LastActiveStance].grid:Hide()
												--print("Hiding last stance: " .. tostring(self.LastActiveStance))
											end
										end
										
										if changed == 1 then
											self.LastActiveStance = new_active
										end
										
										if self.LastActiveStance > 0 and changed == 1 then
											local icon,name,active = GetShapeshiftFormInfo(self.LastActiveStance)
											self.StanceButtons[self.LastActiveStance].icon:SetTexture(icon)
											if not self.StanceButtons[self.LastActiveStance].grid:IsShown() then
												--print("Showing " .. tostring(self.LastActiveStance))
												self.StanceButtons[self.LastActiveStance].grid:Show()
											end
										end
									  end
end

local function SetupContainment()
	local stance = SWF2_Header_Frame.LastActiveStance or 0
	SWF2_Header_Frame:Execute("lastActiveStance = " .. stance ..";")
	
	SWF2_Header_Frame:Execute("SSFI = newtable()")
	for i = 1, GetNumShapeshiftForms() do
		local icon,name,active = GetShapeshiftFormInfo(i)
		SWF2_Header_Frame:Execute("SSFI[" .. i .. "] = \"" .. name .. "\"")
	end
	
	SWF2_Header_Frame:SetAttribute("_onstate-stance", [[
		-- print("Stance: " .. tostring(newstate) .. "Last: " .. tostring(lastActiveStance))
		if newstate == nil or newstate == "" then
			newstate = 0
		end
		
		if tonumber(newstate) ~= lastActiveStance then
			control:CallMethod("UpdateButtons",tonumber(newstate))
		end
		lastActiveStance = tonumber(newstate)
		
		if lastActiveStance > 0 then
			self:SetID(lastActiveStance)
			self:SetAttribute("type", "spell")
			self:SetAttribute("spell",SSFI[lastActiveStance])
			self:SetAttribute("unit", "player")
		else
			self:SetAttribute("type", nil)
			self:SetAttribute("spell",nil)
			self:SetAttribute("unit", nil)
		end
		
	]])
	
	SWF2_Header_Frame:SetAttribute("_onattributechanged", [[
		-- print("Attribute: " .. tostring(name) .. " Value: " .. tostring(value))
	]])
end

local function CreateFormButton(index,icon,name,prevBtn,active)
	local formBtn = SWF2_Header_Frame.StanceButtons[index]
	if formBtn == nil then
		formBtn = CreateFrame("CheckButton","SwitchFFormButton"..index,SWF2_Header_Frame,"SecureActionButtonTemplate")
	end
	
	if InCombatLockdown() == nil then
		formBtn:SetWidth(32)
		formBtn:SetHeight(32)
		formBtn:SetMovable(1)
		formBtn:SetToplevel(1)
		formBtn:ClearAllPoints()
		formBtn:SetWidth(32)
		formBtn:SetHeight(32)
		formBtn:SetPoint("LEFT",prevBtn,"RIGHT",0,0)
		formBtn:SetFrameStrata("LOW")
		formBtn:SetID(index)
		formBtn:SetAttribute("type" ,"spell")
		formBtn:SetAttribute("spell",name)
		formBtn:SetAttribute("unit" ,"player")
	end
	
	local iconTexture = formBtn.icon
	if iconTexture == nil then
		iconTexture = formBtn:CreateTexture(formBtn:GetName().."Icon","BACKGROUND")
	end
	iconTexture:SetTexture(icon)
	iconTexture:SetAllPoints(formBtn)
	formBtn.icon = iconTexture
	
	--print("Creating overlay")
	local t = formBtn.grid
	if t == nil then
		t = formBtn:CreateTexture(nil,"OVERLAY")
	end
	t:SetBlendMode("ADD")
	t:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
	t:SetAllPoints(formBtn)
	t:Hide()
	t:SetTexCoord(.25,.75,.25,.75)
	formBtn.grid = t
	if active == 1 then
		t:Show()
	end
	
	formBtn.icon = iconTexture
	
	SWF2_Header_Frame.StanceButtons[index] = formBtn
	
	return formBtn
end

local gaveWarning = nil;

local function CreateUI()
	-- print("Creating ui")
	if GetNumShapeshiftForms() > 0 then
		-- CreateHeaderFrame()
		
		if InCombatLockdown() == nil then
			SWF2_Header_Frame:Show()
		end
		
		SWF2_Header_Frame.LastActiveStance = nil
		
		local stanceStr = "[stance:0] 0;"
		local lastBtn = SWF2_Header_Frame
		for i = 1, GetNumShapeshiftForms() do
			local icon,name,active = GetShapeshiftFormInfo(i)
			lastBtn = CreateFormButton(i,icon,name,lastBtn,active)
			if active then
				SWF2_Header_Frame.LastActiveStance = i
				--lastBtn.grid:Show()
			end
			stanceStr = stanceStr .. "[stance:"..i.."] "..i..";"
		end
		
		RegisterStateDriver(SWF2_Header_Frame,"stance",stanceStr)
		SetupContainment()
		
		if GetSavedVar("LOCKED",0) == 1 then
			SWF2_Lock()
		end
	else
		if gaveWarning == nil then
			gaveWarning = 1
			print("SwitchF2 disable character has no forms.")
		end
	end
end

local function TabMerge(Src,Dest)
	for k,v in pairs(Src) do
		if Dest[k] == nil then
			Dest[k] = Src[k]
		end
	end
end

local function SetupKeys()
	local Keys = GetSavedVar("statetokey",DEFAULT_KEYS)
	TabMerge(DEFAULT_KEYS,Keys)

	SWF2_Header_Frame:Execute("self:ClearBindings()")
	
	local nButtons = #SWF2_Header_Frame.StanceButtons
	for i = 1,nButtons do
		if i > 0 then
			SWF2_Header_Frame:Execute("self:SetBindingClick(true,\"" .. Keys[i] .. "\",\"" .. SWF2_Header_Frame.StanceButtons[i]:GetName() .. "\");")
			--SetOverrideBindingClick(SWF2_Header_Frame,true,Keys[i],SWF2_Header_Frame.StanceButtons[i]:GetName())
		end
	end
	
	if Keys[0] ~= nil then
		SWF2_Header_Frame:Execute("self:SetBindingClick(true,\"" .. Keys[0] .. "\",\"" .. SWF2_Header_Frame:GetName() .. "\");")
		-- SetOverrideBindingClick(SWF2_Header_Frame,true,Keys[0],SWF2_Header_Frame:GetName())
	end

end

CreateHeaderFrame() --- creating this here so it gets position by the saved state

--- ##################################################### Command line controls

SWF2_Lock = function()
	SetSavedVar("LOCKED",1)
	SWF2_Header_Frame:RegisterForDrag(nil)
	print("Lock SwitchF2 frame")
end

SWF2_UnLock = function()
	SetSavedVar("LOCKED",0)
	SWF2_Header_Frame:RegisterForDrag("LeftButton")
	print("Unlocked SwitchF2 frame")
end

local function SWF2_SlashCmd(cmd)
	if string.lower(cmd) == "lock" then
		SWF2_Lock()
	elseif string.lower(cmd) == "unlock" then
		SWF2_UnLock()
	else
		print("You can use /swf2 lock/unlock to lock the frame without using the graphical config")
		SWF2_Toggle_Configure();
	end
end

local function SetupCmd()
	SlashCmdList["SWF2"] = SWF2_SlashCmd;
	SLASH_SWF21 = "/switchF2" 
	SLASH_SWF22 = "/swf2" 
end

--- ##################################################### Event Frame .. ie loading

local eventFrame = CreateFrame("Frame","SwitchFEventFrame",UIParent,nil)
eventFrame:RegisterEvent("ADDON_LOADED")
eventFrame:RegisterEvent("PLAYER_LOGIN")
eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
eventFrame:RegisterEvent("SPELLS_CHANGED")
eventFrame:RegisterEvent("UNIT_AURA")
eventFrame:SetScript("OnEvent", function (self,event,...)
									if (event == "ADDON_LOADED" and select(1,...) == "SwitchF2") then
										local val = GetSavedVar(SWF2_Header_Frame:GetName() .. "_scale",1)
										SWF2_Header_Frame:SetScale(val)
									elseif (event == "PLAYER_LOGIN") then
										if Loaded == false then
											Loaded = true
											CreateUI()
											SetupKeys()
											SetupCmd()
										end
									elseif (event == "PLAYER_ENTERING_WORLD") then
											SWF2_Header_Frame.UpdateTime = 0
											CreateUI()
									elseif (event == "SPELLS_CHANGED") then
										if InCombatLockdown() == nil then
											CreateUI()
											SetupKeys()
										end
									elseif (event == "UNIT_AURA") then
										local unit = select(1,...)
										if unit == "player" then
											CheckAura()
										end
									end
								end)
								
-- #################################################### Config section

local SWF2_Config_Frame  = nil
local SWF2_Keybind_Frame = nil

local function pad_config_onUpdate(self,elapsed)
	local newScale = self.slider:GetValue()/100;
	if (apBar_LastScale == nil) then
		apBar_LastScale = self.control_parent:GetScale();
	end
	if (newScale ~= apBar_LastScale) then
		self.slider.text:SetText("Scale " .. newScale*100 ..  "%");
		
		local setL = floor(self.control_parent:GetLeft() * self.control_parent:GetScale() +0.5);
		local setT = floor(self.control_parent:GetTop() * self.control_parent:GetScale() + 0.5); -- note this is the distance from the top of the window to the bottom of the screen
		-- so to get the thing in TL respective coords UIParent:GetHeight()-setT should be the distance from the top
		setT = UIParent:GetHeight() - setT;
		
		self.control_parent:SetScale(newScale);
		self.control_parent:ClearAllPoints();
		self.control_parent:SetPoint("TOPLEFT",UIParent,"TOPLEFT", setL/self.control_parent:GetScale(),-setT/self.control_parent:GetScale());
		
		SetSavedVar(self.control_parent:GetName() .. "_scale",newScale);
		apBar_LastScale = newScale
	end
end

local function CreateConfigScale(name, control_parent)
	local cfg_pad = CreateFrame("Frame",name, UIParent, nil);
	cfg_pad.control_parent = control_parent;
	cfg_pad:SetWidth(250);
	cfg_pad:SetHeight(70);
	local bg = cfg_pad:CreateTexture(nil, "BACKGROUND");
	bg:SetTexture(0.5,0.5,0.5,0.5);
	bg:SetAllPoints(cfg_pad);
	
	cfg_pad:SetScript("OnUpdate",pad_config_onUpdate);
	cfg_pad:Hide();

	local lbl = cfg_pad:CreateFontString(nil, "TEXT");
	lbl:SetFontObject(GameFontNormalSmall);
	lbl:SetPoint("BOTTOMLEFT", cfg_pad, "TOPLEFT", 5, -10);
	lbl:SetText("Configuration");
	
	cfg_pad.slider = CreateFrame("Slider",name.."_scale", cfg_pad, "OptionsSliderTemplate");
	cfg_pad.slider:SetPoint("TOPLEFT",cfg_pad,"TOPLEFT",25,-20);
	cfg_pad.slider:SetWidth(cfg_pad:GetWidth() - 50);
	cfg_pad.slider:SetOrientation("HORIZONTAL");
	cfg_pad.slider:SetMinMaxValues(50,200);
	cfg_pad.slider:SetValue(control_parent:GetScale() * 100);
	cfg_pad.slider:SetValueStep(10);
	cfg_pad.slider.text = getglobal(cfg_pad.slider:GetName() .. "Text");
	cfg_pad.slider.text:SetText("Scale " .. cfg_pad.slider:GetValue() ..  "%");
	return cfg_pad;
end

local function KeyBindFrame_KeyUp(self,char)
	if (self.TrapKey and self.FormIndex) then
		if (IsShiftKeyDown()) then
			char = "SHIFT-" .. char
		elseif (IsAltKeyDown()) then
			char = "ALT-" .. char
		elseif (IsControlKeyDown()) then
			char = "CTRL-" .. char
		end
		self.BindingHash[self.FormIndex] = char
		self.LastBind.CurKeyText = char
		
		self.TrapKey   = false
		self.FormIndex = nil
		self:EnableKeyboard(false)
		
		self.LastBind.Revert(self.LastBind)
		self.LastBind  = nil
	end
end

local FORM_CONFIG_LINE_HEIGHT  = 23
local FORM_CONFIG_LINE_PADDING = 3
local FORM_CONFIG_LINE_TOTAL_HEIGHT = FORM_CONFIG_LINE_HEIGHT + (2*FORM_CONFIG_LINE_PADDING)

local function CreateFormBindingLine(index,name,parent,curKeyText)
	local YPos = -((index * FORM_CONFIG_LINE_TOTAL_HEIGHT)+FORM_CONFIG_LINE_PADDING);

	local label = parent:CreateFontString(tostring(index) .. "label", "TEXT");
	label:SetFontObject(GameFontNormalSmall);
	local fontName, fontHeight, fontFlags = label:GetFont()
	label:SetPoint("TOPLEFT", parent, "TOPLEFT", 3, YPos);
	label:SetText(name)
	
	local btn = CreateFrame("Button", tostring(index) .. "button" , parent, "GameMenuButtonTemplate");
	local fontName, fontHeight, fontFlags = label:GetFont()
	btn:SetWidth(50)
	btn:SetNormalFontObject("GameFontHighlight");
	local font = btn:GetNormalFontObject();
	font:SetTextColor(.7, .7, 0.7, 1.0);
	btn:SetNormalFontObject(font)
	btn:SetPoint("TOPLEFT", btn:GetParent(), "TOPRIGHT",-btn:GetWidth(),YPos)
	btn:SetText(curKeyText)
	btn.FormIndex  = index
	btn.CurKeyText = curKeyText
	btn.Revert = function(self)
					self:SetText(btn.CurKeyText)
					self:SetNormalFontObject("GameFontHighlight");
					local font = self:GetNormalFontObject()
					font:SetTextColor(.7,.7,.7,1)
					self:SetNormalFontObject(font)
				 end
	
	btn:SetScript("OnClick",
		function (self)
			self:SetText("...");
			local font = self:GetNormalFontObject()
			font:SetTextColor(1,0,1);
			self:SetNormalFontObject(font)
			self:GetParent().TrapKey = true
			self:GetParent().FormIndex = self.FormIndex
			
			if (self:GetParent().LastBind ~= nil) then
				self:GetParent().LastBind.Revert(self:GetParent().LastBind)
			end
			self:GetParent().LastBind = self
			self:GetParent():EnableKeyboard(true)
		end)
		
	return btn
end

local function CreateFormBindingLines(parent)
	if parent.BindingLines[0] == nil then
		parent.BindingLines[0] = CreateFormBindingLine(0,"Caster",parent,parent.BindingHash[0] or DEFAULT_KEYS[0])
	end
	
	for i = 1, GetNumShapeshiftForms() do
		local icon,name,active = GetShapeshiftFormInfo(i)
		parent.BindingLines[i] = CreateFormBindingLine(i,name,parent,parent.BindingHash[i] or DEFAULT_KEYS[i])
	end
end

local function DupTable(tab)
	local ret = {}
	for i,k in pairs(tab) do
		ret[i] = k
	end
	return ret
end

local function CreateKeyBindFrame(name,relTo)
	frame = CreateFrame("Frame", name, UIParent, nil);
	frame:SetPoint("TOPLEFT",relTo,"TOPLEFT",0,-relTo:GetHeight())
	frame:SetFrameStrata("DIALOG")
	frame:SetWidth(300)
	frame:SetHeight( (GetNumShapeshiftForms()+1) * FORM_CONFIG_LINE_TOTAL_HEIGHT)
	frame:SetPoint("TOPLEFT",relTo,"BOTTOMLEFT",0,0)
	local bg = frame:CreateTexture(nil, "BACKGROUND")
	bg:SetTexture(0.5,0.5,0.5,0.5)
	bg:SetAllPoints(frame)
	frame:SetScript("OnKeyUp",KeyBindFrame_KeyUp)
	frame:Hide()
	frame.BindingLines = {}
	frame.BindingHash  = DupTable(GetSavedVar("statetokey",DEFAULT_KEYS))
	frame.LastBind     = nil
	frame.TrapKey      = false
	frame.FormIndex    = nil
	return frame
end

local function ResetBindingText(parent)
	for i,btn in pairs(parent.BindingLines) do
		btn.CurKeyText = parent.BindingHash[i] or DEFAULT_KEYS[i]
		btn.Revert(btn)
	end
end

local function CreateCancelDone(parent)
	local done = CreateFrame("Button", "Done" , parent, "GameMenuButtonTemplate")
	done:SetHeight(25)
	done:SetWidth(50)
	done:SetPoint("TOPLEFT",parent,"BOTTOMLEFT",0,0)
	done:SetText("Ok")
	done:SetScript("OnClick",
		function (self)
			SetSavedVar("statetokey",self:GetParent().BindingHash)
			SetupKeys()
			SWF2_Toggle_Configure()
		end);
	
	if (cancel == nil) then
		cancel = CreateFrame("Button", "Cancel" , parent, "GameMenuButtonTemplate")
		cancel:SetHeight(25)
		cancel:SetWidth(50)
		cancel:SetPoint("TOPRIGHT",parent,"BOTTOMRIGHT",0,0)
		cancel:SetText("Cancel")
		cancel:SetScript("OnClick",
			function (self)
				self = self:GetParent()
				if (self.LastBind ~= nil) then
					self.TrapKey   = false
					self.FormIndex = nil
					self.LastBind.Revert(self.LastBind)
					self.LastBind = nil;
				end
				self.BindingHash = DupTable(GetSavedVar("statetokey",DEFAULT_KEYS))
				self:EnableKeyboard(false)
				ResetBindingText(self)
				SWF2_Toggle_Configure()
			end)
	end
	
end

SWF2_Toggle_Configure = function()
	if not SWF2_Config_Frame then
		SWF2_Config_Frame = CreateConfigScale("SWF2_Scale_Frame",SWF2_Header_Frame)
	end
	if not SWF2_Keybind_Frame then
		SWF2_Keybind_Frame = CreateKeyBindFrame("SWF2_Keybind_Frame",SWF2_Header_Frame)
		CreateFormBindingLines(SWF2_Keybind_Frame)
		CreateCancelDone(SWF2_Keybind_Frame)
	end
	
	if SWF2_Config_Frame:IsVisible() then
		SWF2_Config_Frame:Hide()
		SWF2_Keybind_Frame:Hide()
		SWF2_Header_Frame:RegisterForDrag("LeftButton")
	else
		SWF2_Header_Frame:RegisterForDrag(nil)
		
		local pX = SWF2_Config_Frame.control_parent:GetLeft() * SWF2_Config_Frame.control_parent:GetScale()
		local pY = SWF2_Config_Frame.control_parent:GetScale() * SWF2_Config_Frame.control_parent:GetTop()
		SWF2_Config_Frame:SetPoint("TOPLEFT",UIParent,"TOPLEFT", pX, -(SWF2_Config_Frame:GetParent():GetTop() - pY)  + SWF2_Config_Frame:GetHeight())
		SWF2_Config_Frame:Show()
		
		SWF2_Keybind_Frame:Show()
	end
end

